package cl.loso.melon.server.util;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
import java.lang.Boolean;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.appengine.api.datastore.DatastoreFailureException;
import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.FetchOptions;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Transaction;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.appengine.api.memcache.Expiration;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;

public class Util {
	private static Log logger = LogFactory.getLog(Util.class);
	private static DatastoreService datastore = DatastoreServiceFactory
			.getDatastoreService();
	private static MemcacheService keycache = MemcacheServiceFactory
			.getMemcacheService();

	public static Object[] split(String cadena, String separador) {
		StringTokenizer st = new StringTokenizer(cadena, separador);
		Vector<String> v = new Vector<String>();
		while (st.hasMoreTokens()) {
			String token = (String) st.nextToken();
			v.add(token);
			// System.out.println(token);
		}
		return v.toArray();
	}

	public static Date getPrimerDiaDelMes(int num) {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MONTH, num);

		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.getActualMinimum(Calendar.DAY_OF_MONTH),
				cal.getMinimum(Calendar.HOUR_OF_DAY),
				cal.getMinimum(Calendar.MINUTE),
				cal.getMinimum(Calendar.SECOND));
		return cal.getTime();
	}

	public static Date getUltimoDiaDelAnoAnterior() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.YEAR, -1);
		cal.set(Calendar.MONTH, Calendar.DECEMBER);

		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.getActualMaximum(Calendar.DAY_OF_MONTH),
				cal.getMaximum(Calendar.HOUR_OF_DAY),
				cal.getMaximum(Calendar.MINUTE),
				cal.getMaximum(Calendar.SECOND));
		return cal.getTime();
	}

	public static Date getUltimoDiaDelMes(int num) {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MONTH, num);

		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.getActualMaximum(Calendar.DAY_OF_MONTH),
				cal.getMaximum(Calendar.HOUR_OF_DAY),
				cal.getMaximum(Calendar.MINUTE),
				cal.getMaximum(Calendar.SECOND));
		return cal.getTime();
	}

	public static Date getPrimerDiaDelAno() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MONTH, -cal.get(Calendar.MONTH));

		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.getActualMinimum(Calendar.DAY_OF_MONTH),
				cal.getMinimum(Calendar.HOUR_OF_DAY),
				cal.getMinimum(Calendar.MINUTE),
				cal.getMinimum(Calendar.SECOND));
		return cal.getTime();
	}

	public static Date getUltimoDiaDelAno() {
		Calendar cal = Calendar.getInstance();
		cal.add(Calendar.MONTH, 11 - cal.get(Calendar.MONTH));

		cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH),
				cal.getActualMaximum(Calendar.DAY_OF_MONTH),
				cal.getMaximum(Calendar.HOUR_OF_DAY),
				cal.getMaximum(Calendar.MINUTE),
				cal.getMaximum(Calendar.SECOND));
		return cal.getTime();
	}

	/**
	 * 
	 * @param entity
	 *            : entity to be persisted
	 */
	public static void persistEntity(Entity entity) {
		Transaction txn = null;
		try {
			txn = datastore.beginTransaction();
			datastore.put(entity);
			txn.commit();
		} catch (IllegalArgumentException e) {
			logger.error(e.getMessage());
		} catch (ConcurrentModificationException e) {
			logger.error(e.getMessage());
		} catch (DatastoreFailureException e) {
			logger.error(e.getMessage());
		} catch (IllegalStateException e) {
			logger.error(e.getMessage());
		} finally {
			if (txn.isActive()) {
				txn.rollback();
			}
		}
	}

	/**
	 * Search and return the entity from the datastore.
	 * 
	 * @param key
	 *            : key to find the entity
	 * @return entity
	 */
	public static Entity findEntity(Key key) {
		try {
			return datastore.get(key);
		} catch (EntityNotFoundException e) {
			logger.error("devolviendo un nulo");
			return null;
		}
	}

	/***
	 * Search entities based on search criteria
	 * 
	 * @param kind
	 * @param searchBy
	 *            : Searching Criteria (Property)
	 * @param searchFor
	 *            : Searching Value (Property Value)
	 * @return List all entities of a kind from the cache or datastore (if not
	 *         in cache) with the specified properties
	 */
	public static Iterable<Entity> listAllEntities(String kind) {

		Query q = new Query(kind);

		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listEntitiesHome(String kind,
			String searchBy, String searchFor) {

		Query q = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		q.addSort("negocio", SortDirection.ASCENDING);
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listEntities(String kind, String searchBy,
			String searchFor) {

		Query q = new Query(kind);
		if (searchFor != null && !"".equals(searchFor)) {
			q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listEntities2(String kind, String searchBy,
			Long searchFor) {

		Query q = new Query(kind);
		if (searchFor != null) {
			q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listUsuariosPorId(String kind,
			String searchBy, Long searchFor) {

		Query q = new Query(kind);
		q.setKeysOnly();
		if (searchFor != null) {
			q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listEntities3(String kind, String searchBy,
			Long searchFor, Date fecini, Date fecter) {
		Query q = new Query(kind);
		if (searchFor != null) {
			q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listVisitaItem(String kind, Date fecini,
			Date fecter) {
		Query q = new Query(kind);

		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> listEntitiesKeys(String kind) {
		Query q = new Query(kind);
		q.setKeysOnly();
		PreparedQuery pq = datastore.prepare(q);
		return pq.asIterable();
	}

	/**
	 * Get the list of children from a parent key in the entity group
	 * 
	 * @param kind
	 *            : the entity kind of the children that is to be searched for
	 * @param ancestor
	 *            : the parent key of the entity group where we need to search
	 * @return iterable with all children of the parent of the specified kind
	 */
	public static Iterable<Entity> listChildren(String kind, Key ancestor) {
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.addFilter(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor);
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	public static Iterable<Entity> listChildrenOnlyKeys(String kind,
			Key ancestor) {
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.setKeysOnly();
		query.addFilter(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor);
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}
	
	public static int cantidadDeHijos(String kind,
			Key ancestor, Date fecini,Date fecter) {
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.setKeysOnly();
		
		//query.addFilter(Entity.KEY_RESERVED_PROPERTY,
		//		FilterOperator.GREATER_THAN, ancestor);
		query.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		query.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		
		PreparedQuery pq = datastore.prepare(query);
		FetchOptions fetchOptions = FetchOptions.Builder
				.withLimit(Integer.MAX_VALUE);
		
		int rowCount = pq.countEntities(fetchOptions);
		return rowCount;
	}	
	
	public static int cantidadDeHijosConSitio(String kind,
			Key ancestor, long sitio,Date fecini,Date fecter) {
		Query query = new Query(kind);
		query.setAncestor(ancestor);
		query.setKeysOnly();
		
		//query.addFilter(Entity.KEY_RESERVED_PROPERTY,
		//		FilterOperator.GREATER_THAN, ancestor);
		query.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		query.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		query.addFilter("sitio", FilterOperator.EQUAL, sitio);
		
		PreparedQuery pq = datastore.prepare(query);
		FetchOptions fetchOptions = FetchOptions.Builder
				.withLimit(Integer.MAX_VALUE);
		
		int rowCount = pq.countEntities(fetchOptions);
		return rowCount;
	}	

	public static Iterable<Entity> listUsuariosConVisitas(String kind,
			Date fecini, Date fecter) {

		Query q = new Query(kind);
		q.setKeysOnly();
		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	/**
	 * Get the list of keys of all children for a given entity kind in a given
	 * entity group represented by the parent key
	 * 
	 * @param kind
	 *            : Entity kind of the children that needs to be searched for
	 * @param ancestor
	 *            : Parent key of the entity group that needs to be searched for
	 * @return an iterable with keys of children
	 */
	public static Iterable<Entity> listChildKeys(String kind, Key ancestor) {

		Query query = new Query(kind);
		query.setAncestor(ancestor).setKeysOnly();
		query.addFilter(Entity.KEY_RESERVED_PROPERTY,
				FilterOperator.GREATER_THAN, ancestor);
		PreparedQuery pq = datastore.prepare(query);
		return pq.asIterable();
	}

	public static void deleteEntities(String kind) {
		Query query = new Query(kind);
		query.setKeysOnly();
		for (Entity entity : datastore.prepare(query).asIterable()) {
			datastore.delete(entity.getKey());
		}
	}

	public static void deleteAllEntities(String kind, String searchBy,
			Long searchFor) {
		Query query = new Query(kind);
		query.setKeysOnly();
		if (searchFor != null) {
			query.addFilter(searchBy, FilterOperator.EQUAL, searchFor);
		}
		for (Entity entity : datastore.prepare(query).asIterable()) {
			datastore.delete(entity.getKey());

		}
	}

	public static Iterable<Entity> DuracionVisita(String kind, Date fecini,
			Date fecter, Long idUsuario) {

		Query q = new Query(kind);

		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		q.addFilter("idUsuario", FilterOperator.EQUAL, idUsuario);

		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static ArrayList<Long> totalesVisita(String kind, Date fecini,
			Date fecter, String searchBy, Long  searchFor ) {

		long tseguro = 0;
		long tinseguro = 0;
		long tduracion = 0;

		Query q = new Query(kind);

		ArrayList<Long> resultado = new ArrayList<Long>();

		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		q.addFilter(searchBy, FilterOperator.EQUAL, searchFor);

		PreparedQuery pq = datastore.prepare(q);

		Iterable<Entity> entities = pq.asIterable();

		for (Entity e : entities) {
			tseguro += ((Long) e.getProperty("tseguro")).longValue();

			tinseguro += ((Long) e.getProperty("tinseguro")).longValue();

			tduracion += ((Long) e.getProperty("duracion")).longValue();

		}

		resultado.add(tseguro);
		resultado.add(tinseguro);
		resultado.add(tduracion);

		return resultado;
	}

	/*
	 * public static Float obtenerTotalMeta(Date fecha_ini, Date fecha_ter,
	 * String idNegocio) throws Exception { float meta = 0; List<UsuarioLVS>
	 * usuarioList = UsuarioLVSDAO
	 * .buscarUsuarioPorNegocio(Long.valueOf(idNegocio));
	 * 
	 * if (!usuarioList.isEmpty()) { Iterator<UsuarioLVS> ite =
	 * usuarioList.iterator(); while (ite.hasNext()) { UsuarioLVS usuario =
	 * ite.next();
	 * 
	 * meta = meta + MetaLVSDAO.obtenerTotalMeta(fecha_ini, fecha_ter,
	 * usuario).floatValue(); } } log.info("negocio: " + idNegocio +
	 * " total meta: " + meta); return meta;
	 * 
	 * }
	 */
	public static double totalMeta(Date fecha_ini, Date fecha_ter,
			Iterable<Entity> usuarios) {
		float lameta = 0;
		Calendar _calini = Calendar.getInstance();
		_calini.setTime(fecha_ini);
		Calendar _calfin = Calendar.getInstance();
		_calfin.setTime(fecha_ter);
		
		for (Entity usuario : usuarios) {
			Key key = usuario.getKey();

			Query q = new Query("MetaLVS");
			
			q.setAncestor(key);

			q.addFilter("mes", FilterOperator.GREATER_THAN_OR_EQUAL,
					_calini.get(Calendar.MONTH));

			q.addFilter("mes", FilterOperator.LESS_THAN_OR_EQUAL, _calfin.get(Calendar.MONTH));
			
			q.addFilter("ano", FilterOperator.EQUAL, _calini.get(Calendar.YEAR));
			
			PreparedQuery pq = datastore.prepare(q);
			
			Iterable<Entity> metas=pq.asIterable();
			
			for(Entity meta:metas){
				lameta += ((Double)meta.getProperty("meta")).doubleValue();
			}

		}

		return lameta;

	}

	public static Iterable<Entity> totalTemas(String kind, Date fecini,
			Date fecter, Long idTema) {

		Query q = new Query(kind);

		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		q.addFilter("item", FilterOperator.EQUAL, idTema);

		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}

	public static Iterable<Entity> totalTemasPorNegocio(String kind,
			Date fecini, Date fecter, Long idNegocio) {

		Query q = new Query(kind);

		q.addFilter("fecha", FilterOperator.GREATER_THAN_OR_EQUAL, fecini);
		q.addFilter("fecha", FilterOperator.LESS_THAN_OR_EQUAL, fecter);
		q.addFilter("negocio", FilterOperator.EQUAL, idNegocio);

		PreparedQuery pq = datastore.prepare(q);

		return pq.asIterable();
	}
	
	public static Long[] TotalSegurosEInsegurosPorNegocio(Date fecini, Date fecter,
			long idNegocio) {
		Iterable<Entity> temasevaluados = totalTemasPorNegocio(
				"VisitaItemLVS", fecini, fecter, idNegocio);
		long _tseguro = 0;
		long _tinseguro = 0;
		long _teval = 0;
		int _cant=cantidadTemas();

		for (Entity tema : temasevaluados) {
			_tseguro += ((Long) tema.getProperty("tseguro")).longValue();
			_tinseguro += ((Long) tema.getProperty("tinseguro")).longValue();
			/*
			Boolean bseg=(Boolean)tema.getProperty("seguro");
			Boolean binseg=(Boolean)tema.getProperty("inseguro");
			_tseguro += bseg ? 1:0;
			_tinseguro += binseg ? 1:0;
			*/
			
			_teval += ((Long) tema.getProperty("teval")).longValue();
		}

		Long[] resultado = new Long[3];
		resultado[0] = _teval/_cant;
		resultado[1] = _tseguro/_cant;
		resultado[2] = _tinseguro/_cant;

		return resultado;
	}
	
	public static Long[] TotalSegurosEInseguros(Date fecini, Date fecter, long idTema) throws Exception {
	
		Iterable<Entity> temasevaluados = totalTemas("VisitaItemLVS",
				fecini, fecter, idTema);
		//logger.info("idTema evaluao " + idTema);
		long _tseguro = 0;
		long _tinseguro = 0;
		long _teval = 0;

		for (Entity tema : temasevaluados) {
			Boolean bseg=(Boolean)tema.getProperty("seguro");
			Boolean binseg=(Boolean)tema.getProperty("inseguro");
			_tseguro += bseg ? 1:0;
			_tinseguro += binseg ? 1:0;
			//_teval += _tseguro + _tinseguro;
			//_tseguro +=((Long) tema.getProperty("tseguro")).longValue();
			//_tinseguro += ((Long) tema.getProperty("tinseguro")).longValue();
			
			//_teval += ((Long) tema.getProperty("teval")).longValue();
		}

		Long[] resultado = new Long[3];
		resultado[0] = _tseguro+_tinseguro;
		resultado[1] = _tseguro;
		resultado[2] = _tinseguro;

		return resultado;
	}
	
	
	public static int cantidadTemas() {

		Query q = new Query("ItemLVS");
		FetchOptions fetchOptions = FetchOptions.Builder
				.withDefaults();

		PreparedQuery pq = datastore.prepare(q);

		return pq.countEntities(fetchOptions);
	}	

	public static Map<String, String> getQueryMap(String query) {
		String name = null;
		String value = null;
		String[] params = query.split("&");

		Map<String, String> map = new HashMap<String, String>();
		for (String param : params) {
			String[] parcito = param.split("=");

			if (parcito.length == 2) {
				name = parcito[0];
				value = parcito[1];
			} else if (parcito.length == 1) {
				name = parcito[0];
				value = "";
			}

			map.put(name, value);
		}
		return map;
	}

	public static Map<String, String> getQueryMap2(String query,
			String nameParam, String valueParam) {
		String name = null;
		String value = null;
		String[] params = query.split("&");

		Map<String, String> map = new HashMap<String, String>();
		for (String param : params) {

			String[] parcito = param.split("=");

			if (parcito.length > 1) {
				name = parcito[0];
				value = parcito[1];
				if (name.equals(nameParam)) {
					value = valueParam;
				}
			} else if (parcito.length == 1) {
				name = parcito[0];
				value = "";

			}

			map.put(name, value);
		}

		if (nameParam != null && valueParam != null) {
			map.put(nameParam, valueParam);
		}
		return map;
	}

	public static String setQueryMap(Map<String, String> map)
			throws UnsupportedEncodingException {

		StringBuffer sb = new StringBuffer();
		map.remove("offset");

		Set<String> keys = map.keySet();

		for (String key : keys) {

			String valor = map.get(key);
			if (!valor.trim().equals("") && valor != null) {
				sb.append(key);
				sb.append("=");
				sb.append(valor);
				sb.append("&");
			}

		}
		return sb.toString();

	}

	/**
	 * Adds the entity to cache
	 * 
	 * @param key
	 *            : key of the entity
	 * @param entity
	 *            : Entity being added
	 */
	public static void addToCache(Key key, Entity entity) {

		keycache.put(key, entity, Expiration.byDeltaSeconds(3600));
	}

	/**
	 * Delete the entity from cache
	 * 
	 * @param key
	 *            : Key of the entity that needs to be deleted
	 */
	public static void deleteFromCache(Key key) {

		keycache.delete(key);
	}

	/**
	 * Delete entities based on a set of keys
	 * 
	 * @param keys
	 *            : list of keys for the entities that are to be deleted
	 */
	public static void deleteFromCache(List<Key> keys) {
		keycache.deleteAll(keys);
	}

	/**
	 * Search for an entity based on key in the cache
	 * 
	 * @param key
	 *            : key of the entity that is searched for
	 * @return the entity
	 */
	public static Entity getFromCache(Key key) {

		return (Entity) keycache.get(key);
	}
}
